
import { resolve } from 'path'
import { isExists } from "#utils/fsV"
import type { Api, Config, UnicloudUrlOriginEvent, UnicloudEvent, MiddlewareMain, RouterContext } from '#types/unicloud';
import compose from 'koa-compose'
import { parse } from 'url'
export class Router {
    private config: Config
    constructor(config: Config) {
        this.config = config || {}
    }
    private isJSON<T = string | Record<string, unknown>>(input: T): T {
        if (typeof input === 'string') {
            try {
                const obj = JSON.parse(input);
                if (typeof obj == 'object' && obj) {
                    return obj;
                }
            } catch (e) {
                // 抛出错误
                throw new Error(`${input} is not a valid json`);
            }
        }
        return input;
    }
    async serve(event: UnicloudEvent | UnicloudUrlOriginEvent, context: RouterContext) {
        // 捕获全局错误
        try {
            let ev = event as UnicloudEvent
            // 判断是否是函数url化
            if (context.SOURCE === 'http') {
                const e = event as UnicloudUrlOriginEvent; // 那么event就是集成请求
                if (e.isBase64Encoded) { // 判断是否是base64编码
                    e.body = Buffer.from(e.body, 'base64').toString(); // 对body进行base64解码
                }
                ev = {
                    route: e.path.replace('/', ''), //去除首个/
                    method: e.httpMethod as any,
                    data: e.httpMethod === 'GET' ? e.queryStringParameters : e.body === '' ? {} : (this.isJSON(e.body) as any)   // body如果是空字符串, 就给一个空对象, 反之需要isJSON转换
                }
            } else {
                // url上的参数复制到data
                const urlQuery = parse(ev.route, true)
                ev.route = urlQuery.pathname as string
                ev.data = {
                    ...ev.data,
                    ...urlQuery.query
                }
            }
            // 解构前端上传数据
            const { route, method, data: params } = ev
            // 获取前端请求接口文件
            const api_name = route.replace(/(\/)/g, '.')
            if (isExists(resolve(__dirname, `./module/${api_name}.js`)) !== 'file') {
                // 查无API文件
                return {
                    errCode: 404,
                    errMsg: `${route} is not defined`
                }
            }
            const file_data = require(resolve(__dirname, `./module/${api_name}.js`)).main as Api
            // 判断接口可读取状态
            if (!file_data) {
                // 查无API文件
                return {
                    errCode: 404,
                    errMsg: `${route} is not defined`
                }
            }
            if (!file_data.enable) {
                // 该API未开启
                return {
                    errCode: 403,
                    errMsg: `${route} is not defined`
                }
            }
            // 判断接口请求类型是否正确   值不存在时默认get，post均可
            if (typeof file_data.method === 'string') {
                if (file_data.method !== method) {
                    return {
                        errCode: 500,
                        errMsg: `The method ${method} is not the correct method`
                    }
                }
            }
            // 获取中间件
            const { middleware = [], ts, tsbufferValidator } = this.config

            // 获取校验json文件
            if (ts && tsbufferValidator && file_data.validate) {
                const json_file = require(resolve(__dirname, `./schema/TSBuffer.json`))
                if (json_file[route]) {
                    const plugins = require(resolve(__dirname, "./plugins/index.js"))
                    const strict = Object.prototype.toString.call(file_data.strict) === "[object Boolean]" ? file_data.strict : true
                    const validateFn = plugins.validate(json_file[route], params, strict)
                    if (validateFn.code !== 0) {
                        return {
                            errCode: 500,
                            errMsg: validateFn.msg
                        }
                    }
                }
            }
            context.event = event
            context.data = ev.data // 原content添加前端请求参数
            context.throw = (code: string | number, msg: string) => {
                throw {
                    code,
                    message: msg
                }
            }
            let pre_composeFn: MiddlewareMain[] = [] // 需要执行的前置中间件数组
            let post_composeFn: MiddlewareMain[] = [] // 需要执行的后置中间件数组
            middleware.sort((a, b) => a.sort - b.sort) // 按顺序排列
            middleware.forEach((item) => {
                //是否开启
                if (item.enable) {
                    if (item.mode === 'preApiCall') {
                        //前置中间件
                        if (item.ignore && item.ignore.length > 0) { //是否有过滤
                            const isIgnore = item.ignore.some((it) => it.test(ev.route)) //当前路由是否符合过滤条件
                            if (isIgnore) return
                        }
                        pre_composeFn.push(item.main)
                    } else if (item.mode === 'postApiCall') {
                        //后置中间件
                        post_composeFn.push(item.main)
                    }
                }
            })
            const fn = compose(pre_composeFn)
            return new Promise((resolve) => {
                fn(context).then(async () => {
                    const response = await file_data.handler(ev, context)
                    resolve(response)
                }).catch(err => {
                    resolve(this.failed(err))
                })
            })
        } catch (err: any) {
            return this.failed(err)
        }

    }
    private failed(err: any) {
        const codeType = typeof err.code === 'number' || typeof err.code === 'string'
        return {
            errCode: codeType ? err.code : err.name || 500,
            errMsg: err.message || "未知错误"
        }
    }
}